﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;

//FastInvoke invoke = new FastInvoke(@"D:\NET\Jerry\Lin.Web\Lib\Sys.Model.dll", "Sys.Model.Test",ref instance, "Sub");
namespace Lin.Invoke {
    public class FastInvoke {

        private Func<object, object[], object> m_execute;

        public FastInvoke(MethodInfo methodInfo) {
            this.m_execute = this.GetExecuteDelegate(methodInfo);
        }

        /// <summary>
        /// 快速反射构造
        /// </summary>
        /// <param name="instance">实例</param>
        /// <param name="method">方法名</param>
        public FastInvoke(object instance,string method) {
            var info = instance.GetType().GetMethod(method);
            this.m_execute = this.GetExecuteDelegate(info);
        }

        /// <summary>
        /// 快速反射构造
        /// </summary>
        /// <param name="dllPath">DLL地址</param>
        /// <param name="funllName">命名空间加类名</param>
        /// <param name="instance">返回创建的实例</param>
        /// <param name="method">要调用的方法名称</param>
        public FastInvoke(string dllPath, string funllName,ref object instance, string method,bool isResources=false,object[] args=null) {
            if (!isResources)
            {
                var asse = Assembly.LoadFile(dllPath);
                var type = asse.GetType(funllName);//必须使用名称空间+类名称
                var inst = instance = asse.CreateInstance(funllName);
                var info = type.GetMethod(method);
                this.m_execute = this.GetExecuteDelegate(info);
            }
            else {
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(dllPath))
                {

                    Byte[] assemblyData = new Byte[stream.Length];

                    stream.Read(assemblyData, 0, assemblyData.Length);

                    var asse = Assembly.Load(assemblyData);

                    var flag = (System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
                    var cult = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name);

                    var type = asse.GetType(funllName);//必须使用名称空间+类名称
                    var inst = instance = asse.CreateInstance(funllName, false, flag, null, args, cult, new object[] { });
                    var info = type.GetMethod(method);
                    this.m_execute = this.GetExecuteDelegate(info);

                    //Type[] type = assembly.GetTypes();
                    //MethodInfo mi = type[0].GetMethod("Add");
                    //if (null != mi)
                    //{
                    //    object obj = Activator.CreateInstance(type[0]);
                    //    var aa = mi.Invoke(obj, new object[] { 1, 2 });
                    //}

                }
            }

            
        }

        public object Execute(object instance, object[] parameters) {
            return this.m_execute(instance, parameters);
        }

        private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo) {
            // parameters to execute
            ParameterExpression instanceParameter =
                Expression.Parameter(typeof(object), "instance");
            ParameterExpression parametersParameter =
                Expression.Parameter(typeof(object[]), "parameters");

            // build parameter list
            List<Expression> parameterExpressions = new List<Expression>();
            ParameterInfo[] paramInfos = methodInfo.GetParameters();
            for (int i = 0; i < paramInfos.Length; i++) {
                // (Ti)parameters[i]
                BinaryExpression valueObj = Expression.ArrayIndex(
                    parametersParameter, Expression.Constant(i));
                UnaryExpression valueCast = Expression.Convert(
                    valueObj, paramInfos[i].ParameterType);

                parameterExpressions.Add(valueCast);
            }

            // non-instance for static method, or ((TInstance)instance)
            Expression instanceCast = methodInfo.IsStatic ? null :
                Expression.Convert(instanceParameter, methodInfo.ReflectedType);

            // static invoke or ((TInstance)instance).Method
            MethodCallExpression methodCall = Expression.Call(
                instanceCast, methodInfo, parameterExpressions);

            // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
            if (methodCall.Type == typeof(void)) {
                Expression<Action<object, object[]>> lambda =
                    Expression.Lambda<Action<object, object[]>>(
                        methodCall, instanceParameter, parametersParameter);

                Action<object, object[]> execute = lambda.Compile();
                return (instance, parameters) => {
                    execute(instance, parameters);
                    return null;
                };
            }
            else {
                UnaryExpression castMethodCall = Expression.Convert(
                    methodCall, typeof(object));
                Expression<Func<object, object[], object>> lambda =
                    Expression.Lambda<Func<object, object[], object>>(
                        castMethodCall, instanceParameter, parametersParameter);

                return lambda.Compile();
            }
        }
    
    }
}
